﻿/*** MikeWare Framework ************************************
* This class is part of product of MikeWare.Framework.
* 
* Author      : Mike Cheers
* Mail        : mikecheers@126.com
* Create Date : 2018/9/14
* Summary     : 
* 
* 
* Modified By : 
* Date        : 
* Mail        : 
* Comment     : 
**************************************************************/

namespace MikeWare.Core.Utilities
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading;

    /// <summary>
    /// Provides methods to write/read a stream.
    /// </summary>
    public static class StreamHelper
    {
        #region [ Write Value ]
        /// <summary>
        /// Write a bool value into stream
        /// </summary>
        /// <param name="stream">The stream to be written.</param>
        /// <param name="value">The value to write.</param>
        public static void WriteValue(Stream stream, Boolean value)
        {
            WriteValue(stream, value, true);
        }
        /// <summary>
        /// Write a bool value into stream
        /// </summary>
        /// <param name="stream">The stream to be written.</param>
        /// <param name="value">The value to write.</param>
        /// <param name="flush">Indicate wether flush the stream immediately.</param>
        public static void WriteValue(Stream stream, Boolean value, bool flush)
        {
            byte[] buffer = System.BitConverter.GetBytes(value);
            WriteValue(stream, buffer, flush);
        }

        /// <summary>
        /// Write a Char value into stream
        /// </summary>
        /// <param name="stream">The stream to be written.</param>
        /// <param name="value">The value to write.</param>
        public static void WriteValue(Stream stream, Char value)
        {
            WriteValue(stream, value, true);
        }

        /// <summary>
        /// Write a Char value into stream
        /// </summary>
        /// <param name="stream">The stream to be written.</param>
        /// <param name="value">The value to write.</param>
        /// <param name="flush">Indicate wether flush the stream immediately.</param>
        public static void WriteValue(Stream stream, Char value, bool flush)
        {
            byte[] buffer = System.BitConverter.GetBytes(value);
            WriteValue(stream, buffer, flush);
        }

        /// <summary>
        /// Write a Double value into stream
        /// </summary>
        /// <param name="stream">The stream to be written.</param>
        /// <param name="value">The value to write.</param>
        public static void WriteValue(Stream stream, Double value)
        {
            WriteValue(stream, value, true);
        }

        /// <summary>
        /// Write a Double value into stream
        /// </summary>
        /// <param name="stream">The stream to be written.</param>
        /// <param name="value">The value to write.</param>
        /// <param name="flush">Indicate wether flush the stream immediately.</param>
        public static void WriteValue(Stream stream, Double value, bool flush)
        {
            byte[] buffer = System.BitConverter.GetBytes(value);
            WriteValue(stream, buffer, flush);
        }

        /// <summary>
        /// Write a float value into stream
        /// </summary>
        /// <param name="stream">The stream to be written.</param>
        /// <param name="value">The value to write.</param>
        public static void WriteValue(Stream stream, float value)
        {
            WriteValue(stream, value, true);
        }

        /// <summary>
        /// Write a float value into stream
        /// </summary>
        /// <param name="stream">The stream to be written.</param>
        /// <param name="value">The value to write.</param>
        /// <param name="flush">Indicate wether flush the stream immediately.</param>
        public static void WriteValue(Stream stream, float value, bool flush)
        {
            byte[] buffer = System.BitConverter.GetBytes(value);
            WriteValue(stream, buffer, flush);
        }

        /// <summary>
        /// Write a Int32 value into stream
        /// </summary>
        /// <param name="stream">The stream to be written.</param>
        /// <param name="value">The value to write.</param>
        public static void WriteValue(Stream stream, Int32 value)
        {
            WriteValue(stream, value, true);
        }

        /// <summary>
        /// Write a Int32 value into stream
        /// </summary>
        /// <param name="stream">The stream to be written.</param>
        /// <param name="value">The value to write.</param>
        /// <param name="flush">Indicate wether flush the stream immediately.</param>
        public static void WriteValue(Stream stream, Int32 value, bool flush)
        {
            byte[] buffer = System.BitConverter.GetBytes(value);
            WriteValue(stream, buffer, flush);
        }

        /// <summary>
        /// Write a Int64 value into stream
        /// </summary>
        /// <param name="stream">The stream to be written.</param>
        /// <param name="value">The value to write.</param>
        public static void WriteValue(Stream stream, Int64 value)
        {
            WriteValue(stream, value, true);
        }

        /// <summary>
        /// Write a Int64 value into stream
        /// </summary>
        /// <param name="stream">The stream to be written.</param>
        /// <param name="value">The value to write.</param>
        /// <param name="flush">Indicate wether flush the stream immediately.</param>
        public static void WriteValue(Stream stream, Int64 value, bool flush)
        {
            byte[] buffer = System.BitConverter.GetBytes(value);
            WriteValue(stream, buffer, flush);
        }

        /// <summary>
        /// Write a short value into stream
        /// </summary>
        /// <param name="stream">The stream to be written.</param>
        /// <param name="value">The value to write.</param>
        public static void WriteValue(Stream stream, short value)
        {
            WriteValue(stream, value, true);
        }

        /// <summary>
        /// Write a short value into stream
        /// </summary>
        /// <param name="stream">The stream to be written.</param>
        /// <param name="value">The value to write.</param>
        /// <param name="flush">Indicate wether flush the stream immediately.</param>
        public static void WriteValue(Stream stream, short value, bool flush)
        {
            byte[] buffer = System.BitConverter.GetBytes(value);
            WriteValue(stream, buffer, flush);
        }

        /// <summary>
        /// Write a UInt32 value into stream
        /// </summary>
        /// <param name="stream">The stream to be written.</param>
        /// <param name="value">The value to write.</param>
        public static void WriteValue(Stream stream, UInt32 value)
        {
            WriteValue(stream, value, true);
        }

        /// <summary>
        /// Write a UInt32 value into stream
        /// </summary>
        /// <param name="stream">The stream to be written.</param>
        /// <param name="value">The value to write.</param>
        /// <param name="flush">Indicate wether flush the stream immediately.</param>
        public static void WriteValue(Stream stream, UInt32 value, bool flush)
        {
            byte[] buffer = System.BitConverter.GetBytes(value);
            WriteValue(stream, buffer, flush);
        }

        /// <summary>
        /// Write a UInt64 value into stream
        /// </summary>
        /// <param name="stream">The stream to be written.</param>
        /// <param name="value">The value to write.</param>
        public static void WriteValue(Stream stream, UInt64 value)
        {
            WriteValue(stream, value, true);
        }

        /// <summary>
        /// Write a UInt64 value into stream
        /// </summary>
        /// <param name="stream">The stream to be written.</param>
        /// <param name="value">The value to write.</param>
        /// <param name="flush">Indicate wether flush the stream immediately.</param>
        public static void WriteValue(Stream stream, UInt64 value, bool flush)
        {
            byte[] buffer = System.BitConverter.GetBytes(value);
            WriteValue(stream, buffer, flush);
        }

        /// <summary>
        /// Write a DateTime value into stream
        /// </summary>
        /// <param name="stream">The stream to be written.</param>
        /// <param name="value">The value to write.</param>
        public static void WriteValue(Stream stream, DateTime value)
        {
            WriteValue(stream, value, true);
        }

        /// <summary>
        /// Write a DateTime value into stream
        /// </summary>
        /// <param name="stream">The stream to be written.</param>
        /// <param name="value">The value to write.</param>
        /// <param name="flush">Indicate wether flush the stream immediately.</param>
        public static void WriteValue(Stream stream, DateTime value, bool flush)
        {
            WriteValue(stream, value.ToBinary(), flush);
        }

        /// <summary>
        /// Write a ushort value into stream
        /// </summary>
        /// <param name="stream">The stream to be written.</param>
        /// <param name="value">The value to write.</param>
        public static void WriteValue(Stream stream, ushort value)
        {
            WriteValue(stream, value, true);
        }

        /// <summary>
        /// Write a ushort value into stream
        /// </summary>
        /// <param name="stream">The stream to be written.</param>
        /// <param name="value">The value to write.</param>
        /// <param name="flush">Indicate wether flush the stream immediately.</param>
        public static void WriteValue(Stream stream, ushort value, bool flush)
        {
            byte[] buffer = System.BitConverter.GetBytes(value);
            WriteValue(stream, buffer, flush);
        }

        /// <summary>
        /// Write a string value into stream
        /// </summary>
        /// <param name="stream">The stream to be written</param>
        /// <param name="value">The value to write</param>
        /// <param name="encoding">The encoding info of the string value</param>
        public static void WriteValue(Stream stream, string value, Encoding encoding)
        {
            WriteValue(stream, value, encoding, true);
        }

        /// <summary>
        /// Write a string value into stream
        /// </summary>
        /// <param name="stream">The stream to be written</param>
        /// <param name="value">The value to write</param>
        /// <param name="encoding">The encoding info of the string value</param>
        /// <param name="flush">Indicate wether flush the stream immediately.</param>
        public static void WriteValue(Stream stream, string value, Encoding encoding, bool flush)
        {
            if (null == value) value = string.Empty;

            byte[] buffer = encoding.GetBytes(value);
            WriteValue(stream, buffer.Length, false);
            WriteValue(stream, buffer, false);
            if (flush) Flush(stream);
        }

        /// <summary>
        /// Write a string value into stream
        /// </summary>
        /// <param name="stream">The stream to be written</param>
        /// <param name="value">The value to write</param>
        /// <param name="encoding">The encoding info of the string value</param>
        public static void WriteValue(Stream stream, IEnumerable<string> values, Encoding encoding)
        {
            WriteValue(stream, values, encoding, true);
        }

        /// <summary>
        /// Write a string value into stream
        /// </summary>
        /// <param name="stream">The stream to be written</param>
        /// <param name="value">The value to write</param>
        /// <param name="encoding">The encoding info of the string value</param>
        /// <param name="flush">Indicate wether flush the stream immediately.</param>
        public static void WriteValue(Stream stream, IEnumerable<string> values, Encoding encoding, bool flush)
        {
            if (null == values || 0 == values.Count())
            {
                WriteValue(stream, 0);
            }
            else
            {
                WriteValue(stream, values.Count(), false);
                foreach (var value in values)
                {
                    WriteValue(stream, value, encoding, false);
                }
                if (flush) Flush(stream);
            }
        }

        /// <summary>
        /// Write a byte array value into stream
        /// </summary>
        /// <param name="stream">The stream to be written.</param>
        /// <param name="value">The value to write.</param>
        public static void WriteValue(Stream stream, byte[] value)
        {
            WriteValue(stream, value, true);
        }

        /// <summary>
        /// Write a byte array value into stream
        /// </summary>
        /// <param name="stream">The stream to be written.</param>
        /// <param name="value">The value to write.</param>
        /// <param name="flush">Indicate wether flush the stream immediately.</param>
        public static void WriteValue(Stream stream, byte[] value, bool flush)
        {
            stream.Write(value, 0, value.Length);
            if (flush) Flush(stream);
        }

        public static void Flush(Stream stream)
        {
            stream.Flush();
        }
        #endregion [ Write Value ]

        #region [ Read Value ]
        /// <summary>
        /// Read a short int value from stream BinaryReader.
        /// </summary>
        /// <param name="reader">The stream BinaryReader instance.</param>
        /// <returns>short value</returns>
        public static Int16 ReadInt16(BinaryReader reader)
        {
            return reader.ReadInt16();
        }

        /// <summary>
        /// Read a int value from stream BinaryReader.
        /// </summary>
        /// <param name="reader">The stream BinaryReader instance.</param>
        /// <returns>int value</returns>
        public static int ReadInt32(BinaryReader reader)
        {
            return reader.ReadInt32();
        }

        /// <summary>
        /// Read a Int64 value from stream BinaryReader.
        /// </summary>
        /// <param name="reader">The stream BinaryReader instance.</param>
        /// <returns>Int64 value</returns>
        public static Int64 ReadInt64(BinaryReader reader)
        {
            return reader.ReadInt64();
        }

        /// <summary>
        /// Read a double value from stream BinaryReader.
        /// </summary>
        /// <param name="reader">The stream BinaryReader instance.</param>
        /// <returns>double value</returns>
        public static Double ReadDouble(BinaryReader reader)
        {
            return reader.ReadDouble();
        }

        /// <summary>
        /// Read a Boolean value from stream BinaryReader.
        /// </summary>
        /// <param name="reader">The stream BinaryReader instance.</param>
        /// <returns>Boolean value</returns>
        public static Boolean ReadBoolean(BinaryReader reader)
        {
            return reader.ReadBoolean();
        }

        /// <summary>
        /// Read a byte array value from stream BinaryReader.
        /// </summary>
        /// <param name="reader">The stream BinaryReader instance.</param>
        /// <returns>byte array value</returns>
        public static byte[] ReadBytes(BinaryReader reader, int count)
        {
            return reader.ReadBytes(count);
        }

        public static byte[] Read16Bytes(BinaryReader reader)
        {
            var count = ReadInt16(reader);
            return reader.ReadBytes(count);
        }

        public static byte[] Read32Bytes(BinaryReader reader)
        {
            var count = ReadInt32(reader);
            return reader.ReadBytes(count);
        }

        /// <summary>
        /// Read a String value from stream BinaryReader.
        /// </summary>
        /// <param name="reader">The stream BinaryReader instance.</param>
        /// <param name="encoding">The encoding of the value.</param>
        /// <returns>String value</returns>
        public static String ReadString(BinaryReader reader, Encoding encoding)
        {
            int size = ReadInt32(reader);
            return ReadString(reader, size, encoding);
        }
        public static String ReadString(BinaryReader reader, int count, Encoding encoding)
        {
            return encoding.GetString(ReadBytes(reader, count));
        }
        public static String Read16String(BinaryReader reader, Encoding encoding)
        {
            int size = ReadInt16(reader);
            return ReadString(reader, size, encoding);
        }

        /// <summary>
        /// Read a String enumerable value from stream BinaryReader.
        /// </summary>
        /// <param name="reader">The stream BinaryReader instance.</param>
        /// <param name="encoding">The encoding of the value.</param>
        /// <returns>String enumerable value</returns>
        public static IEnumerable<string> ReadStringList(BinaryReader reader, Encoding encoding)
        {
            int count = ReadInt32(reader);
            if (0 == count) return null;
            var result = new Result<string>();
            for (int index = 0; index < count; index++)
            {
                result.Add(ReadString(reader, encoding));
            }
            return result;
        }

        /// <summary>
        /// Read a DateTime value from stream BinaryReader.
        /// </summary>
        /// <param name="reader">The stream BinaryReader instance.</param>
        /// <returns>DateTime value</returns>
        public static DateTime ReadDateTime(BinaryReader reader)
        {
            return DateTime.FromBinary(reader.ReadInt64());
        }

        public static Byte ReadUInt8(BinaryReader reader)
        {
            return reader.ReadByte();
        }

        public static UInt32 ReadUInt16(BinaryReader reader)
        {
            return reader.ReadUInt16();
        }

        public static UInt32 ReadUInt32(BinaryReader reader)
        {
            return reader.ReadUInt32();
        }
        #endregion [ Read Value ]

        public static StreamReader GetFileReaderUntilAvailable(String filePath)
        {
            return GetFileReaderUntilAvailable(filePath, Encoding.Default);
        }

        public static StreamReader GetFileReaderUntilAvailable(String filePath, Encoding encoding)
        {
            StreamReader reader = null;
            do
            {
                try
                {
                    reader = new StreamReader(filePath, encoding);
                }
                catch (FileNotFoundException ex)
                {
                    reader = null;
                    Trace.WriteLine("@" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " - GetFileReaderUntilAvailable: " + filePath + " - " + ex.Message);
                    break;
                }
                catch (IOException ex)
                {
                    reader = null;
                    Trace.WriteLine("@" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " - GetFileReaderUntilAvailable: " + ex.Message);
                    Thread.Sleep(1000);
                }
                catch (Exception ex)
                {
                    reader = null;
                    Trace.WriteLine(ex.ToString());
                    break;
                }
            } while (null == reader);

            return reader;
        }

        public static StreamWriter GetFileWriterUntilAvailable(Stream stream, Encoding encoding)
        {
            StreamWriter writer = null;
            do
            {
                try
                {
                    writer = new StreamWriter(stream, encoding);
                }
                catch (FileNotFoundException ex)
                {
                    writer = null;
                    Trace.WriteLine("@" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " - GetFileWriterUntilAvailable: <stream> - " + ex.Message);
                    break;
                }
                catch (IOException ex)
                {
                    writer = null;
                    Trace.WriteLine("@" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " - GetFileWriterUntilAvailable: " + ex.Message);
                    Thread.Sleep(1000);
                }
                catch (Exception ex)
                {
                    writer = null;
                    Trace.WriteLine(ex.ToString());
                    break;
                }
            } while (null == writer);

            return writer;
        }

        public static StreamWriter GetFileWriterUntilAvailable(String filePath, Boolean append, Encoding encoding)
        {
            StreamWriter writer = null;
            do
            {
                try
                {
                    writer = new StreamWriter(filePath, append, encoding);
                }
                catch (FileNotFoundException ex)
                {
                    writer = null;
                    Trace.WriteLine("@" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " - GetFileWriterUntilAvailable: " + filePath + " - " + ex.Message);
                    break;
                }
                catch (IOException ex)
                {
                    writer = null;
                    Trace.WriteLine("@" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " - GetFileWriterUntilAvailable: " + ex.Message);
                    Thread.Sleep(1000);
                }
                catch (Exception ex)
                {
                    writer = null;
                    Trace.WriteLine(ex.ToString());
                    break;
                }
            } while (null == writer);

            return writer;
        }

        public static BinaryReader GetBinaryReaderUntilAvailable(String filePath)
        {
            return GetBinaryReaderUntilAvailable(filePath, Encoding.Default);
        }

        public static BinaryReader GetBinaryReaderUntilAvailable(String filePath, Encoding encoding)
        {
            BinaryReader reader = null;
            do
            {
                try
                {
                    reader = new BinaryReader(File.OpenRead(filePath), encoding);
                }
                catch (FileNotFoundException ex)
                {
                    reader = null;
                    Trace.WriteLine("@" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " - GetFileReaderUntilAvailable: " + filePath + " - " + ex.Message);
                    break;
                }
                catch (IOException ex)
                {
                    reader = null;
                    Trace.WriteLine("@" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " - GetFileReaderUntilAvailable: " + ex.Message);
                    Thread.Sleep(1000);
                }
                catch (Exception ex)
                {
                    reader = null;
                    Trace.WriteLine(ex.ToString());
                    break;
                }
            } while (null == reader);

            return reader;
        }

        public static BinaryWriter GetBinaryWriterUntilAvailable(Stream stream, Encoding encoding)
        {
            BinaryWriter writer = null;
            do
            {
                try
                {
                    writer = new BinaryWriter(stream, encoding);
                }
                catch (FileNotFoundException ex)
                {
                    writer = null;
                    Trace.WriteLine("@" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " - GetFileWriterUntilAvailable: <stream> - " + ex.Message);
                    break;
                }
                catch (IOException ex)
                {
                    writer = null;
                    Trace.WriteLine("@" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " - GetFileWriterUntilAvailable: " + ex.Message);
                    Thread.Sleep(1000);
                }
                catch (Exception ex)
                {
                    writer = null;
                    Trace.WriteLine(ex.ToString());
                    break;
                }
            } while (null == writer);

            return writer;
        }

        public static BinaryWriter GetBinaryWriterUntilAvailable(String filePath, Boolean append, Encoding encoding)
        {
            BinaryWriter writer = null;
            do
            {
                try
                {
                    writer = new BinaryWriter(File.Open(filePath, append ? FileMode.Append : FileMode.OpenOrCreate, FileAccess.Write), encoding);
                }
                catch (FileNotFoundException ex)
                {
                    writer = null;
                    Trace.WriteLine("@" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " - GetFileWriterUntilAvailable: " + filePath + " - " + ex.Message);
                    break;
                }
                catch (IOException ex)
                {
                    writer = null;
                    Trace.WriteLine("@" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " - GetFileWriterUntilAvailable: " + ex.Message);
                    Thread.Sleep(1000);
                }
                catch (Exception ex)
                {
                    writer = null;
                    Trace.WriteLine(ex.ToString());
                    break;
                }
            } while (null == writer);

            return writer;
        }
    }
}
